package com.cctc.rds.raft.sdk;

import com.cctc.rds.raft.core.RaftCluster;
import com.cctc.rds.raft.core.client.command.RaftCommandClient;
import com.cctc.rds.raft.core.client.election.RaftElectionStarter;
import com.cctc.rds.raft.core.log.LogService;
import com.cctc.rds.raft.core.log.commit.statemachine.StateMachine;
import com.cctc.rds.raft.core.node.NodeIpPort;
import com.cctc.rds.raft.core.rpc.RpcClientRouter;
import com.cctc.rds.raft.core.rpc.election.ElectionRpc;
import com.cctc.rds.raft.core.rpc.log.LogRpc;
import com.cctc.rds.raft.core.util.SignalManager;
import com.cctc.rds.raft.sdk.client.RaftClient;
import com.cctc.rds.raft.sdk.client.rpc.ElectionRpcClientRouter;
import com.cctc.rds.raft.sdk.client.rpc.LogRpcClientRouter;
import com.cctc.rds.raft.sdk.server.RaftServer;

import java.util.Set;

public class RaftStarter {

    public static RaftCommandClient start(int nodeId,
                                          LogService logService,
                                          StateMachine stateMachine,
                                          Set<NodeIpPort> nodes) {
        // 1. 启动服务端
        RaftServer raftServer = new RaftServer(nodeId, logService, stateMachine, nodes);
        raftServer.start();

        // 2. 启动客户端
        RaftClient raftClient = new RaftClient(nodeId);
        raftClient.connect(nodes, RaftCluster.getConfig());

        // 3. 发送 [心跳 | 日志] 请求
        RpcClientRouter<LogRpc> logRpcClientRouter = new LogRpcClientRouter(nodeId, raftClient);
        RaftCommandClient raftCommandClient = new RaftCommandClient(nodeId, logService, stateMachine, logRpcClientRouter);

        // 4. 发送 [选主] 请求
        RpcClientRouter<ElectionRpc> electionRpcClientRouter = new ElectionRpcClientRouter(nodeId, raftClient);
        RaftElectionStarter electionStarter = new RaftElectionStarter(nodeId, logService, electionRpcClientRouter, raftCommandClient);

        // 5. 注册 kill 钩子
        SignalManager.addFirst(() -> {
            try {
                electionStarter.stop();
                raftCommandClient.close();
                raftClient.close();
                raftServer.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        return raftCommandClient;
    }

}
